package io.cucumber.core.runner;

import com.zws.cucumber.ZwsUtils;
import io.cucumber.core.backend.Glue;
import io.cucumber.core.backend.StepDefinition;

import java.util.*;
import java.util.function.Consumer;

public class CachingGlueHelper {
    // stepDefinitions
    // stepDefinitionsByPattern
    public static void modify(Glue glue) {
        if (!(glue instanceof CachingGlue)) {
             throw new IllegalArgumentException("glue should be CachingGlue, but was " + glue);
        }

        List<StepDefinition> originOfStepDefinitions = ZwsUtils.getFieldValue(
                CachingGlue.class, "stepDefinitions", glue);
        Map<String, CoreStepDefinition> originOfStepDefinitionsByPattern = ZwsUtils.getFieldValue(
                CachingGlue.class, "stepDefinitionsByPattern", glue);

        ZwsUtils.setFieldValue(CachingGlue.class, "stepDefinitions", glue,
                new ListWrapper<>(originOfStepDefinitions) {
                    @Override
                    public void forEach(Consumer<? super StepDefinition> action) {
                        if (originOfStepDefinitionsByPattern.isEmpty()) {
                            super.forEach(action);
                        }
                    }
                });
        ZwsUtils.setFieldValue(CachingGlue.class, "stepDefinitionsByPattern", glue,
                new MapWrapper<>(originOfStepDefinitionsByPattern) {
                    @Override
                    public void clear() {
                        // noop
                    }
                });
    }

    public static class ListWrapper<T> implements List<T> {

        private List<T> origin;

        public ListWrapper(List<T> origin) {
            this.origin = origin;
        }

        @Override
        public Iterator<T> iterator() {
            return origin.iterator();
        }

        @Override
        public void forEach(Consumer<? super T> action) {
            origin.forEach(action);
        }

        @Override
        public int size() {
            return origin.size();
        }

        @Override
        public boolean isEmpty() {
            return origin.isEmpty();
        }

        @Override
        public boolean contains(Object o) {
            return origin.contains(o);
        }

        @Override
        public Object[] toArray() {
            return origin.toArray();
        }

        @Override
        public <T1> T1[] toArray(T1[] a) {
            return origin.toArray(a);
        }

        @Override
        public boolean add(T t) {
            return origin.add(t);
        }

        @Override
        public boolean remove(Object o) {
            return origin.remove(o);
        }

        @Override
        public boolean containsAll(Collection<?> c) {
            return origin.containsAll(c);
        }

        @Override
        public boolean addAll(Collection<? extends T> c) {
            return origin.addAll(c);
        }

        @Override
        public boolean addAll(int index, Collection<? extends T> c) {
            return origin.addAll(c);
        }

        @Override
        public boolean removeAll(Collection<?> c) {
            return origin.removeAll(c);
        }

        @Override
        public boolean retainAll(Collection<?> c) {
            return origin.retainAll(c);
        }

        @Override
        public void clear() {
            origin.clear();
        }

        @Override
        public T get(int index) {
            return origin.get(index);
        }

        @Override
        public T set(int index, T element) {
            return origin.set(index, element);
        }

        @Override
        public void add(int index, T element) {
            origin.add(index, element);
        }

        @Override
        public T remove(int index) {
            return origin.remove(index);
        }

        @Override
        public int indexOf(Object o) {
            return origin.indexOf(o);
        }

        @Override
        public int lastIndexOf(Object o) {
            return origin.lastIndexOf(o);
        }

        @Override
        public ListIterator<T> listIterator() {
            return origin.listIterator();
        }

        @Override
        public ListIterator<T> listIterator(int index) {
            return origin.listIterator();
        }

        @Override
        public List<T> subList(int fromIndex, int toIndex) {
            return origin.subList(fromIndex, toIndex);
        }
    }

    public static class MapWrapper<K, V> implements Map<K, V> {

        private Map<K, V> origin;

        public MapWrapper(Map<K, V> origin) {
            this.origin = origin;
        }

        @Override
        public int size() {
            return origin.size();
        }

        @Override
        public boolean isEmpty() {
            return origin.isEmpty();
        }

        @Override
        public boolean containsKey(Object key) {
            return origin.containsKey(key);
        }

        @Override
        public boolean containsValue(Object value) {
            return origin.containsValue(value);
        }

        @Override
        public V get(Object key) {
            return origin.get(key);
        }

        @Override
        public V put(K key, V value) {
            return origin.put(key, value);
        }

        @Override
        public V remove(Object key) {
            return origin.remove(key);
        }

        @Override
        public void putAll(Map<? extends K, ? extends V> m) {
            origin.putAll(m);
        }

        @Override
        public void clear() {
            origin.clear();
        }

        @Override
        public Set<K> keySet() {
            return origin.keySet();
        }

        @Override
        public Collection<V> values() {
            return origin.values();
        }

        @Override
        public Set<Entry<K, V>> entrySet() {
            return origin.entrySet();
        }
    }

}
